package PRO;

import java.math.*;

public class AtaqueRhoPollard {

    BigInteger n, clavePublica;
    BigInteger[] textoCifrado;
    int descifrado;

    //Constructor de un nuevo ataque partiendo de un n, e y el texto cifrado
    public AtaqueRhoPollard(BigInteger numero, BigInteger publica, BigInteger[] texto, int descifradoTipo) {

        n = numero;
        clavePublica = publica;
        textoCifrado = texto;
        descifrado = descifradoTipo;
    }

    public void iniciar() {

        System.out.println("Realizando ataque de factorizacion por Rho...");

        long comienzo = System.nanoTime();
        long comienzo1 = System.currentTimeMillis();

        BigInteger[] factores = rho();

        BigInteger factor1 = factores[0];
        BigInteger factor2 = factores[1];
        System.out.println("Factores de [n] encontrados: " + factor1 + " x " + factor2);

        Rsa nuevoRsa = new Rsa(factor1, factor2, clavePublica, descifrado);
        String mensajeRoto = nuevoRsa.descifrar(textoCifrado);

        System.out.println("Valor Euler decifrado: " + nuevoRsa.vareulerF());
        System.out.println("Valor de la llave privada: " + nuevoRsa.vard());
        System.out.println("El mensaje fue: " + mensajeRoto);

        long fin = System.nanoTime();
        long fin1 = System.currentTimeMillis();
        long tiempo = (fin - comienzo);
        long tiempo1 = (fin1 - comienzo1);

        System.out.println("El proceso de ataque se ejecutó en " + tiempo + " nanosegundos");
        System.out.println("Redondeado en " + tiempo1 + " milisegundos");

    }

    public BigInteger[] rho() {

        // Valores iniciales de a y b para iniciar el algoritmo
        BigInteger a = new BigInteger("2");
        BigInteger b = new BigInteger("2");

        BigInteger d = null;

        //Se mantiene en el ciclo hasta que cumpla alguna condicion:
        while (true) {

            // Operate on a once, and b twice.
            a = (a.multiply(a).add(BigInteger.ONE)).mod(n);
            b = (b.multiply(b).add(BigInteger.ONE)).mod(n);
            b = (b.multiply(b).add(BigInteger.ONE)).mod(n);

            // maximo comun divisor
            d = n.gcd(a.subtract(b));

            // salimos del ciclo si se encuentra un factor
            if (!d.equals(BigInteger.ONE) && !d.equals(n)) {
                break;
            }

            // esta condicion nos dice que no hay factores y salimos del ciclo
            if (d.equals(n)) {
                break;
            }
        }

        //Si no hay factores, avisamos
        if (d.equals(n)) {
            System.out.println("No se encontraron factores");
        }
        BigInteger[] factores = new BigInteger[2];
        factores[0] = d;
        factores[1] = n.divide(d);

        //retornamos los factores encontrados
        return factores;
    }
}
